package com.symaster.common.image;

import com.symaster.common.util.IOUtil;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

/**
 * Bmp 图片对象
 *
 * @author yinmiao
 * @date 2021-04-10 18:32
 */
public class BmpHeader {

    private final byte[] bytes = new byte[54];
    private int iSize, ibiSize, iWidth, iHeight, iPlanes, iBitCount, iCompression, iSizeImage, iXpm, iYpm, iClrused, iClrimp;

    public BmpHeader(InputStream fs) throws Exception {
        init(fs);
    }

    public BmpHeader(byte[] bytes) {
        init(bytes);
    }

    public static BmpHeader createHeader(int w, int h, int bitcount) {
        InputStream resourceAsStream = null;
        BmpHeader bmpHeader = null;
        try {
            resourceAsStream = BmpHeader.class.getClassLoader().getResourceAsStream("byte/bmp.sit");
            bmpHeader = new BmpHeader(resourceAsStream);
            bmpHeader.setiWidth(w);
            bmpHeader.setiHeight(h);
            bmpHeader.setiBitCount(bitcount);
        } catch (Exception e) {
            throw new RuntimeException(e);
        } finally {
            IOUtil.close(resourceAsStream);
        }
        return bmpHeader;
    }

    private static short constructShort(byte[] in, int offset) {
        short ret = (short) ((short) in[offset + 1] & 0xff);
        ret = (short) ((ret << 8) | (short) ((short) in[offset] & 0xff));
        return (ret);
    }

    private static int constructInt(byte[] in, int offset) {
        int ret = ((int) in[offset + 3] & 0xff);
        ret = (ret << 8) | ((int) in[offset + 2] & 0xff);
        ret = (ret << 8) | ((int) in[offset + 1] & 0xff);
        ret = (ret << 8) | ((int) in[offset] & 0xff);
        return (ret);
    }

    public void writeHeader(OutputStream os) {
        try {
            os.write(getBytes());
        } catch (Throwable e) {
            throw new RuntimeException(e);
        }
    }

    public void init(InputStream fs) throws IOException {
        if (fs.available() < 54) {
            throw new NullPointerException("fs.available() < 54");
        }
        final int bflen = 14;
        byte[] bf = new byte[bflen];
        fs.read(bf, 0, bflen);
        System.arraycopy(bf, 0, bytes, 0, bf.length);
        final int bilen = 40;
        byte[] bi = new byte[bilen];
        fs.read(bi, 0, bilen);
        System.arraycopy(bi, 0, bytes, bf.length, bi.length);
        iSize = constructInt(bf, 2);
        ibiSize = constructInt(bi, 2);
        iWidth = constructInt(bi, 4);
        iHeight = constructInt(bi, 8);
        iPlanes = constructShort(bi, 12);
        iBitCount = constructShort(bi, 14);
        iCompression = constructInt(bi, 16);
        iSizeImage = constructInt(bi, 20);
        iXpm = constructInt(bi, 24);
        iYpm = constructInt(bi, 28);
        iClrused = constructInt(bi, 32);
        iClrimp = constructInt(bi, 36);
    }

    public void init(byte[] bytes) {
        if (bytes.length < 54) {
            throw new NullPointerException("bytes.length < 54");
        }
        int bfOffset = 0, biOffset = 14;
        iSize = constructInt(bytes, 2 + bfOffset);
        ibiSize = constructInt(bytes, 2 + biOffset);
        iWidth = constructInt(bytes, 4 + biOffset);
        iHeight = constructInt(bytes, 8 + biOffset);
        iPlanes = constructShort(bytes, 12 + biOffset);
        iBitCount = constructShort(bytes, 14 + biOffset);
        iCompression = constructInt(bytes, 16 + biOffset);
        iSizeImage = constructInt(bytes, 20 + biOffset);
        iXpm = constructInt(bytes, 24 + biOffset);
        iYpm = constructInt(bytes, 28 + biOffset);
        iClrused = constructInt(bytes, 32 + biOffset);
        iClrimp = constructInt(bytes, 36 + biOffset);
    }

    public byte[] getBytes() {
        return bytes;
    }

    /**
     * int转byte
     */
    public byte[] intToByteArray(int i) {
        byte[] result = new byte[4];
        // 由高位到低位
        result[0] = (byte) (i & 0xFF);
        result[1] = (byte) ((i >> 8) & 0xFF);
        result[2] = (byte) ((i >> 16) & 0xFF);
        result[3] = (byte) ((i >> 24) & 0xFF);
        return result;
    }

    public int getiSize() {
        return iSize;
    }

    public int getIbiSize() {
        return ibiSize;
    }

    public int getiWidth() {
        return iWidth;
    }

    public void setiWidth(int iWidth) {
        byte[] bytes = intToByteArray(iWidth);
        System.arraycopy(bytes, 0, this.bytes, 18, bytes.length);
        this.iWidth = iWidth;
    }

    public int getiHeight() {
        return iHeight;
    }

    public void setiHeight(int iHeight) {
        byte[] bytes = intToByteArray(iHeight);
        System.arraycopy(bytes, 0, this.bytes, 22, bytes.length);
        this.iHeight = iHeight;
    }

    public int getiPlanes() {
        return iPlanes;
    }

    public int getiBitCount() {
        return iBitCount;
    }

    public void setiBitCount(int iBitCount) {
        byte[] bytes = intToByteArray(iBitCount);
        System.arraycopy(bytes, 0, this.bytes, 28, bytes.length);
        this.iBitCount = iBitCount;
    }

    public int getiCompression() {
        return iCompression;
    }

    public int getiSizeImage() {
        return iSizeImage;
    }

    public int getiXpm() {
        return iXpm;
    }

    public int getiYpm() {
        return iYpm;
    }

    public int getiClrused() {
        return iClrused;
    }

    public int getiClrimp() {
        return iClrimp;
    }

    @Override
    public String toString() {
        return "BmpHeader{" +
                "iSize=" + iSize +
                ", ibiSize=" + ibiSize +
                ", iWidth=" + iWidth +
                ", iHeight=" + iHeight +
                ", iPlanes=" + iPlanes +
                ", iBitCount=" + iBitCount +
                ", iCompression=" + iCompression +
                ", iSizeImage=" + iSizeImage +
                ", iXpm=" + iXpm +
                ", iYpm=" + iYpm +
                ", iClrused=" + iClrused +
                ", iClrimp=" + iClrimp +
                '}';
    }
}
